package com.fallenpanda.location.manager;

import android.app.Application;
import android.content.Context;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;

import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.location.Poi;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.search.core.SearchResult;
import com.baidu.mapapi.search.geocode.GeoCodeResult;
import com.baidu.mapapi.search.geocode.GeoCoder;
import com.baidu.mapapi.search.geocode.OnGetGeoCoderResultListener;
import com.baidu.mapapi.search.geocode.ReverseGeoCodeOption;
import com.baidu.mapapi.search.geocode.ReverseGeoCodeResult;
import com.fallenpanda.location.bean.MyLocation;
import com.fallenpanda.location.utils.StringUtils;

import junit.framework.Assert;

import java.util.Arrays;
import java.util.List;

/**
 * 百度 定位管理器
 * ============================================================================
 * 版权所有 2014 。
 *
 * @author fallenpanda
 *
 * @version 1.0 2014-12-04
 * ============================================================================
 */
public class BaiduLocationManager extends BaseLocationManager {

    private static final String TAG = "BaiduLocationManager";

    public static final String PROVIDER = "baidu";

    private static final Integer[] SUCCESS_CODE = {BDLocation.TypeGpsLocation, BDLocation.TypeNetWorkLocation, BDLocation.TypeOffLineLocation, BDLocation.TypeCacheLocation};
    private static final Integer[] SUCCESS_CODE_ENABLE = {BDLocation.TypeGpsLocation, BDLocation.TypeNetWorkLocation};

    private static final String PRODUCT_NAME = "us";//产品线名称
    private static final String LOCATION_COORTYPE = "bd09ll";//位置信息类型
    private static final int MIN_INTERVAL = 1000;//最小定位间隔

    private Application mContext;
    private MainLocationListener listener;
    private LocationClient client;
    private LocationClientOption clientOption;
    private BDLocationListener locationListener;

    private MyLocation mOffLineLocation;
    private MyLocation mCacheLocation;
    private MyLocation mLastLocation;
    private MyLocation mLocation;

    private GeoCoder mGeoCoder;

    private int mInterval = 0;
    private boolean isNeedAddress = false;

    private static final int MAX_AGAINTIME = 1;
    private int againTime = 0;

    public BaiduLocationManager(Application context, MainLocationListener mainLocationListener) {
        mContext = context;
        SDKInitializer.initialize(context);//百度地图组件

        listener = mainLocationListener;
        locationListener = new BaiduLocationListener();
        client = new LocationClient(context);
        clientOption = getHighAccuracyOption();
        client.setLocOption(clientOption);
        client.registerLocationListener(locationListener);
    }

    /**
     * 返回高精度定位设置（同时使用网络定位和GPS定位，优先返回最高精度的定位结果）
     * 默认：定位一次，无地址信息
     *
     * @return LocationClientOption
     */
    private LocationClientOption getHighAccuracyOption(){
        LocationClientOption locationClientOption = new LocationClientOption();
        locationClientOption.setProdName(PRODUCT_NAME);//产品线名称
        locationClientOption.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);//设置定位模式
        locationClientOption.setOpenGps(true);
        locationClientOption.setCoorType(LOCATION_COORTYPE);//返回的定位结果是百度经纬度,默认值gcj02
        locationClientOption.setScanSpan(mInterval);//小于1000则为一次定位
        locationClientOption.setIsNeedAddress(isNeedAddress);//地址信息
        return locationClientOption;
    }

    private GeoCoder getGeoCoder() {
        if (mGeoCoder == null) {
            mGeoCoder = GeoCoder.newInstance();
            mGeoCoder.setOnGetGeoCodeResultListener(new MyOnGetGeoCoderResultListener());
        }
        return mGeoCoder;
    }

    /**
     * 定位一次
     */
    public void getLocationOnce(boolean isNeedAddress){
        getLocation(MainLocationManager.MODEL_ONCE, 0, isNeedAddress);
    }

    /**
     * 定位多次（暂不支持返回地址信息）
     */
    public void getLocationAuto(int interval, boolean isNeedAddress){
        getLocation(MainLocationManager.MODEL_AUTO, interval, isNeedAddress);
    }

    /**
     * 定位
     *
     * @param mode
     *         MainLocationManager.MODEL_ONCE
     *         MainLocationManager.MODEL_AUTO
     * @param interval 定位间隔
     */
    private void getLocation(int mode,int interval, boolean isNeedAddress){
        if (client != null) {
            switch (mode){
                case MainLocationManager.MODEL_ONCE:
                    if(client.isStarted()){
                        client.requestLocation();
                    }else{
                        updateLocationOptions(0, isNeedAddress);
                    }
                    break;
                case MainLocationManager.MODEL_AUTO:
                    if(interval < MIN_INTERVAL) interval = MIN_INTERVAL;
                    updateLocationOptions(interval, false);
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * 是否开启
     * @return boolean
     */
    public boolean isStarted(){
        return client!=null && client.isStarted();
    }

    /**
     * 开启
     */
    public void start(){
        if (client != null && (!client.isStarted())) {
            client.start();
            onProviderEnabled();
        }
    }

    /**
     * 关闭
     */
    public void stop(){
        if ((client == null) || (!client.isStarted()))
            return;
        client.stop();
        onProviderDisabled();
    }

    /**
     * 销毁
     */
    public void destroy() {
        if (mGeoCoder !=null) {
            mGeoCoder.destroy();
            mGeoCoder = null;
        }
        if (client != null) {
            stop();
            client.unRegisterLocationListener(locationListener);
            client = null;
        }
    }

    @Override
    public void onLocationChanged(MyLocation location) {
        if (listener != null && location != null) {
            mLastLocation = location;
            listener.onLocationChanged(location);
        }
    }

    @Override
    public void onLocationFail(String message) {
        if (listener != null)
            listener.onLocationFail(PROVIDER, message);
    }

    @Override
    public void onStatusChanged(int status, Bundle extras) {
        if (listener != null)
            listener.onStatusChanged(PROVIDER, status, extras);
    }

    @Override
    public void onProviderEnabled() {
        if (listener != null)
            listener.onProviderEnabled(PROVIDER);
    }

    @Override
    public void onProviderDisabled() {
        if (listener != null)
            listener.onProviderDisabled(PROVIDER);
    }

    @Override
    public void updateLocationOptions(int interval, boolean isNeedAddress) {
        if (client != null) {
            stop();
            if(this.mInterval != interval) {
                this.mInterval = interval;
                clientOption.setScanSpan(interval);
            }
            if(this.isNeedAddress != isNeedAddress) {
                this.isNeedAddress = isNeedAddress;
                clientOption.setIsNeedAddress(isNeedAddress);
            }
            client.setLocOption(clientOption);
            start();
        }
    }

    private class BaiduLocationListener implements BDLocationListener {

        @Override
        public void onReceiveLocation(BDLocation location) {
            int errorCode = location.getLocType();
            if(Arrays.asList(SUCCESS_CODE).contains(errorCode)){
                MyLocation localLocation = new MyLocation(PROVIDER);
                localLocation.setmTime(System.currentTimeMillis());
                localLocation.setmCoorType(LOCATION_COORTYPE);
                localLocation.setmLongitude(location.getLongitude());
                localLocation.setmLatitude(location.getLatitude());
                if (location.hasRadius()){
                    localLocation.setmHasAccuracy(true);
                    localLocation.setmAccuracy(location.getRadius());
                }
                if (location.hasAltitude()){
                    localLocation.setmHasAltitude(true);
                    localLocation.setmAltitude(location.getAltitude());
                }
                if (location.getLocType() == BDLocation.TypeGpsLocation){
                    localLocation.setmHasSpeed(true);
                    localLocation.setmSpeed(location.getSpeed());
                } else if (location.getLocType() == BDLocation.TypeNetWorkLocation){
                    if (location.hasAddr()&& StringUtils.isNotEmpty(location.getAddrStr())) {
                        localLocation.setmHasAddress(true);
                        localLocation.setmAddress(location.getAddrStr());
                        localLocation.setmProvince(location.getProvince());
                        localLocation.setmCity(location.getCity());
                        localLocation.setmDistrict(location.getDistrict());
                    }
                }
                if (Arrays.asList(SUCCESS_CODE_ENABLE).contains(errorCode)) {
                    // GPS定位结果 | 网络定位结果
                    onGetLocationSuccess(localLocation);
                } else if (BDLocation.TypeOffLineLocation == errorCode) {
                    // 离线定位结果
                    mOffLineLocation = localLocation;
                    if (!requestLocationAgain())
                        onGetLocationSuccess(localLocation);
                } else if (BDLocation.TypeCacheLocation == errorCode) {
                    // 定位缓存的结果
                    mCacheLocation = localLocation;
                    if (!requestLocationAgain())
                        onGetLocationSuccess(localLocation);
                }
            }else if(BDLocation.TypeServerError == errorCode){
                // 服务端网络定位失败，可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com，会有人追查原因
                onGetLocationFail("服务端网络定位失败，请联系管理员");
            }else if(BDLocation.TypeNetWorkException == errorCode){
                // 网络不同导致定位失败，请检查网络是否通畅
                onGetLocationFail("定位失败，请检查网络是否通畅");
            }else if(BDLocation.TypeCriteriaException == errorCode){
                // 无法获取有效定位依据导致定位失败，一般是由于手机的原因，处于飞行模式下一般会造成这种结果，可以试着重启手机
                onGetLocationFail("定位失败，请检查您是否处于飞行模式，或尝试重启手机");
            }else{
                onGetLocationFail("定位失败，请检查是否允许应用访问定位功能，再重新尝试");
            }

            StringBuffer sb = new StringBuffer(256);
            sb.append("time : ");
            sb.append(location.getTime());
            sb.append("\nerror code : ");
            sb.append(location.getLocType());
            sb.append("\nlatitude : ");
            sb.append(location.getLatitude());
            sb.append("\nlontitude : ");
            sb.append(location.getLongitude());
            sb.append("\nradius : ");
            sb.append(location.getRadius());
            if (location.getLocType() == BDLocation.TypeGpsLocation){// GPS定位结果
                sb.append("\nspeed : ");
                sb.append(location.getSpeed());// 单位：公里每小时
                sb.append("\nsatellite : ");
                sb.append(location.getSatelliteNumber());
                sb.append("\nheight : ");
                sb.append(location.getAltitude());// 单位：米
                sb.append("\ndirection : ");
                sb.append(location.getDirection());// 单位度
                sb.append("\naddr : ");
                sb.append(location.getAddrStr());
                sb.append("\ndescribe : ");
                sb.append("gps定位成功");

            } else if (location.getLocType() == BDLocation.TypeNetWorkLocation){// 网络定位结果
                sb.append("\naddr : ");
                sb.append(location.getAddrStr());
                //运营商信息
                sb.append("\noperationers : ");
                sb.append(location.getOperators());
                sb.append("\ndescribe : ");
                sb.append("网络定位成功");
            } else if (location.getLocType() == BDLocation.TypeOffLineLocation) {// 离线定位结果
                sb.append("\ndescribe : ");
                sb.append("离线定位成功，离线定位结果也是有效的");
            } else if (location.getLocType() == BDLocation.TypeServerError) {
                sb.append("\ndescribe : ");
                sb.append("服务端网络定位失败，可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com，会有人追查原因");
            } else if (location.getLocType() == BDLocation.TypeNetWorkException) {
                sb.append("\ndescribe : ");
                sb.append("网络不同导致定位失败，请检查网络是否通畅");
            } else if (location.getLocType() == BDLocation.TypeCriteriaException) {
                sb.append("\ndescribe : ");
                sb.append("无法获取有效定位依据导致定位失败，一般是由于手机的原因，处于飞行模式下一般会造成这种结果，可以试着重启手机");
            }
            sb.append("\nlocationdescribe : ");
            sb.append(location.getLocationDescribe());// 位置语义化信息
            List<Poi> list = location.getPoiList();// POI数据
            if (list != null) {
                sb.append("\npoilist size = : ");
                sb.append(list.size());
                for (Poi p : list) {
                    sb.append("\npoi= : ");
                    sb.append(p.getId() + " " + p.getName() + " " + p.getRank());
                }
            }
            Log.i("BaiduLocationApiDem", sb.toString());
        }
    }

    private void onGetLocationSuccess(MyLocation location) {
        if(clientOption!=null&&mInterval==0) stop();
        againTime = 0;
        if(isNeedAddress){
            if (location.ismHasAddress()) {
                onLocationChanged(location);
            } else {
                //地理位置反解析
                requestAddress(location);
            }
        }else{
            onLocationChanged(location);
        }
    }

    private void onGetLocationFail(String message) {
        if(clientOption!=null&&mInterval==0) stop();
        againTime = 0;
        MyLocation tempLocation = getTempLocation();
        if (tempLocation != null) {
            if(isNeedAddress){
                if (tempLocation.ismHasAddress()) {
                    onLocationChanged(tempLocation);
                } else {
                    //地理位置反解析
                    requestAddress(tempLocation);
                }
            }else{
                onLocationChanged(tempLocation);
            }
        } else {
            onLocationFail(message);
        }
    }

    private boolean requestLocationAgain() {
        if (mInterval == 0 && againTime < MAX_AGAINTIME) {
            Log.d(TAG, "再次请求，尝试获取更精准位置信息");
            againTime ++;
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    client.requestLocation();
                }
            }, 1500);
            return true;
        }
        return false;
    }

    private void requestAddress(MyLocation location){
        Log.d(TAG, "获取地址信息：latitude" + location.getmLatitude() + "lontitude" + location.getmLongitude());
        if (hasInternet()) {
            mLocation = location;
            LatLng point = new LatLng(location.getmLatitude(), location.getmLongitude());
            getGeoCoder().reverseGeoCode(new ReverseGeoCodeOption().location(point));
        } else {
            onLocationFail("获取地址信息失败，请确保网络通畅！");
        }
    }

    private MyLocation getTempLocation(){
        if (null != mLastLocation && null != mOffLineLocation) {
            return (mOffLineLocation.getmTime() > mLastLocation.getmTime()) ? mOffLineLocation : mLastLocation;
        } else {
            if (null != mLastLocation) return mLastLocation;
            else if (null != mOffLineLocation) return mOffLineLocation;
            else if (null != mCacheLocation) return mCacheLocation;
        }
        return null;
    }

    private class MyOnGetGeoCoderResultListener implements OnGetGeoCoderResultListener {

        @Override
        public void onGetGeoCodeResult(GeoCodeResult geoCodeResult) {
            if (geoCodeResult == null || geoCodeResult.error != SearchResult.ERRORNO.NO_ERROR) {
                //没有检索到结果
            } else {
                //获取地理编码结果
            }
        }

        @Override
        public void onGetReverseGeoCodeResult(ReverseGeoCodeResult reverseGeoCodeResult) {
            if (reverseGeoCodeResult == null || reverseGeoCodeResult.error != SearchResult.ERRORNO.NO_ERROR) {
                //没有找到检索结果
                onLocationFail("获取地址信息失败，请确保网络通畅！");
            } else {
                //获取反向地理编码结果
                mLocation.setmHasAddress(true);
                mLocation.setmAddress(reverseGeoCodeResult.getAddress());
                mLocation.setmProvince(reverseGeoCodeResult.getAddressDetail().province);
                mLocation.setmCity(reverseGeoCodeResult.getAddressDetail().city);
                mLocation.setmDistrict(reverseGeoCodeResult.getAddressDetail().district);
                onLocationChanged(mLocation);

                StringBuffer sb = new StringBuffer(256);
                sb.append(" ");
                sb.append("\naddr : ");
                sb.append(reverseGeoCodeResult.getAddress());
                Log.d(TAG, sb.toString());
            }
        }

    }

    private boolean hasInternet() {
        boolean flag;
        if (((ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo() != null)
            flag = true;
        else
            flag = false;
        return flag;
    }

}
